觀察 index-Start.html 的 javascript 只有以下三行,看來需要利用items
這個陣列的元素去產生結構並於itemList
這個節點中顯示。
const addItems = document.querySelector(".add-items");
const itemsList = document.querySelector(".plates");
// 看到第5點後記得再回來更改
const items = [];
items
中每一項資料的格式吧:// {
// text:String,// 要顯示於頁面上的文字,所以是字串
// done:Bolean,// 用來記錄input是否勾選的狀態,所以是布林值
// }
addItems
這個節點新增一個針對HTMLFormElement: submit event監聽器,事件觸發後會執行addItem
函式,另外因為submit
事件的預設行為會將表單的填入內容添加到網址列並送出,會使頁面重整,但這不是我們想要的效果因此,我們使用Event: preventDefault() method將預設行為禁用,並自己寫要執行的邏輯。addItems.addEventListener("submit", addItem);
function addItem(e) {
// 禁用預設行為
e.preventDefault();
// 從表單內取得添加項目的資料
const text = this.querySelector('input[name="item"]').value;
const item = {
text,
done: false,
};
// 更新至items陣列
items.push(item);
// 清除表單已填的資料
this.reset();
// 看到第5點後記得再回來新增後續邏輯
// .
// .
}
FINISHED.html
新增品項後產生的結構,然後著手改寫每當資料有變更的時候都需要觸發的渲染函式renderItems
吧,這邊我一樣不使用 innerHTML 的方式,全部用 creatElement 並設定屬性方式讓各位看看程式碼數量上的差異。function renderItems(itemsList, items) {
//每次渲染進itemsList之前都先清空內的全部內容
while (itemsList.firstChild) {
itemsList.removeChild(itemsList.firstChild);
}
//創造一個文檔片段
const fragment = document.createDocumentFragment();
//每一項都創造相同的結構:一個<li>裡面有<input>、<label>、<i>
items.forEach((element, index) => {
const li = document.createElement("li");
li.id = index;
//<input>元素
const input = document.createElement("input");
input.type = "checkbox";
input.id = `item${index}`;
input.defaultChecked = element.done;
input.checked = element.done;
li.appendChild(input);
//<label>元素 CSS中作者將input隱藏起來,並使用這個label代替input的功能
const label = document.createElement("label");
//HTML中直接寫的for 若改用node屬性設定則要設定htmlFor
label.htmlFor = `item${index}`;
label.textContent = element.text;
li.appendChild(label);
//<li>元素,我自己額外做的刪除功能不在原題目需求內
//(用到fontawesome icon)請記得在html內引入
const i = document.createElement("i");
i.classList.add("fa-solid", "fa-trash-can");
li.appendChild(i);
fragment.appendChild(li);
});
itemsList.appendChild(fragment);
}
handleInputClick
函式,只有當觸發事件的元素是<label>
或<i>
時才會做出該項 item 的切換狀態或刪除。itemsList.addEventListener("click", handleInputClick);
function handleInputClick(e) {
// 觸發元素是label時,反轉該項的完成Done狀態
if (e.target.matches("label")) {
items[e.target.parentNode.id].done = !items[e.target.parentNode.id].done;
// 看到第5點後記得再回來新增後續邏輯
// .
// .
// 觸發元素是i時,將該項從陣列中移除
} else if (e.target.matches("i")) {
items.splice(e.target.parentNode.id, 1);
// 看到第5點後記得再回來新增後續邏輯
// .
// .
}
}
LocalStorage
只能存放 UTF-16 格式字串,但我們的資料是陣列裝著物件,因此我們在讀取時須透過JSON.parse()
解析資料,儲存時使用JSON.stringfy()
轉成 JSON 字串。// 當頁面載入時如果localstorage已經有資料那就拿過來使用,如果沒有則預設為一個空陣列
const items = JSON.parse(localStorage.getItem("items")) || [];
// 並拿這個items去執行一次渲染函式
renderItems(itemsList, items);
items
陣列資料時(handleInputClick
函式、addItem
函式)後面都補上以下兩句。// 將變動後的資料覆寫進localStorage
localStorage.setItem("items", JSON.stringify(items));
// 再次執行渲染函式,更新畫面
renderItems(itemsList, items);